package psd.model.Prolog;

import java.util.HashMap;

import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeParameter;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;

public class IDManagerIType {

	private HashMap known;
	private IType envClass;
	private IFQNTranslator fqn;
	private IIDGenerator ids;

	public IDManagerIType(IFQNTranslator fqn, IIDGenerator ids) {
		known = new HashMap();
		this.fqn = fqn;
		this.ids = ids;
	}

	public String getID(IType type) throws JavaModelException {
		return fqn.transformFQN((new StringBuilder("fqn('")).append(
				getFullyQualifiedName(type)).append("')").toString());
	}

	public String getID(IMethod method, String type) throws JavaModelException {
		return fqn.transformFQN((new StringBuilder("fqn('")).append(
				getTypeName(method, type)).append("')").toString());
	}

	public String getID(IField field) throws JavaModelException {
		return fqn.transformFQN((new StringBuilder("fqn('")).append(
				getFullyQualifiedName(field.getDeclaringType())).append("', '")
				.append(field.getElementName()).append("')").toString());
	}

	public String getID(IMethod method) throws JavaModelException {
		String name = method.isConstructor() ? "<init>" : method
				.getElementName();
		return fqn.transformFQN((new StringBuilder("fqn('")).append(
				getFullyQualifiedName(method.getDeclaringType()))
				.append("', '").append(name).append("',").append(
						arglist(method, method.getParameterTypes()))
				.append(")").toString());
	}

	private String getFullyQualifiedName(IType type) {
		return type.getFullyQualifiedName('$');
	}

	

	public String newID() {
		return ids.getID();
	}

	private String getSimpleName(String classname) {
		try {
			classname = classname.substring(classname.lastIndexOf("."));
		} catch (IndexOutOfBoundsException _ex) {
		}
		return classname;
	}

	private String arglist(IMethod method, String classes[])
			throws JavaModelException {
		StringBuffer buf = new StringBuffer("[");
		boolean first = true;
		for (int i = 0; i < classes.length; i++) {
			String type = classes[i];
			if (!first) {
				buf.append(", ");
			} else {
				first = false;
			}
			buf.append("'");
			int dim = getArrayDim(type);
			buf.append(getTypeName(method, type.substring(dim)));
			for (; dim > 0; dim--) {
				buf.append("[]");
			}

			buf.append("'");
		}

		buf.append("]");
		return buf.toString();
	}

	public String getTypeName(IMethod enclosingMethod, String s)
			throws JavaModelException {
		switch (s.charAt(0)) {
		case 66: // 'B'
			return "byte";

		case 67: // 'C'
			return "char";

		case 68: // 'D'
			return "double";

		case 70: // 'F'
			return "float";

		case 73: // 'I'
			return "int";

		case 74: // 'J'
			return "long";

		case 76: // 'L'
			return Signature.toString(Signature.getTypeErasure(s));

		case 83: // 'S'
			return "short";

		case 86: // 'V'
			return "void";

		case 90: // 'Z'
			return "boolean";

		case 81: // 'Q'
			String simpleClassName = Signature.getTypeErasure(s);
			return resolveClassNameFromSimpleName(Signature
					.toString(simpleClassName));

		case 84: // 'T'
			String parameter = Signature.toString(s);
			ITypeParameter tp = null;
			if (enclosingMethod != null) {
				tp = enclosingMethod.getTypeParameter(parameter);
			}
			if (enclosingMethod == null || !tp.exists()) {
				tp = envClass.getTypeParameter(parameter);
			}
			IType currentClass = envClass;
			for (; !tp.exists(); tp = currentClass.getTypeParameter(parameter)) {
				currentClass = currentClass.getDeclaringType();
				if (currentClass == null) {
					throw new RuntimeException((new StringBuilder(
							"Unvalid type parameter: ")).append(tp).append(
							"in class ").append(envClass).toString());
				}
			}

			String bounds[] = tp.getBounds();
			if (bounds.length > 0) {
				int typeParamPos = bounds[0].indexOf("<");
				if (typeParamPos > 0) {
					return bounds[0].substring(0, typeParamPos);
				} else {
					return bounds[0];
				}
			} else {
				return "java.lang.Object";
			}

		case 69: // 'E'
		case 71: // 'G'
		case 72: // 'H'
		case 75: // 'K'
		case 77: // 'M'
		case 78: // 'N'
		case 79: // 'O'
		case 80: // 'P'
		case 82: // 'R'
		case 85: // 'U'
		case 87: // 'W'
		case 88: // 'X'
		case 89: // 'Y'
		default:
			throw new RuntimeException((new StringBuilder(
					"can not find type for: ")).append(s).toString());
		}
	}

	private String resolveClassNameFromSimpleName(String simpleClassName)
	        throws JavaModelException
	    {
	        String fqn;
	        String aFqn[] = envClass.resolveType(simpleClassName)[0];
	        if(aFqn.length == 1)
	        {
	            fqn = aFqn[0].replace('.', '$');
	        } else
	        if(aFqn.length == 2)
	        {
	            fqn = (new StringBuilder(String.valueOf(aFqn[0]))).append(".").append(aFqn[1].replace('.', '$')).toString();
	        } else
	        {
	            throw new RuntimeException((new StringBuilder("Unexpected arity of fqn array: ")).append(aFqn.length).append(", ").append(aFqn[0]).toString());
	        }
	        return fqn;
	     
	    }

	public static boolean isPrimitive(String s) {
		switch (s.charAt(0)) {
		case 91: // '['
			throw new RuntimeException((new StringBuilder(
					"no arrayType excpected: ")).append(s).toString());

		case 76: // 'L'
			return false;

		case 81: // 'Q'
			return false;

		case 84: // 'T'
			return false;
		}
		return true;
	}

	public static boolean isTypeParameter(String s) {
		switch (s.charAt(0)) {
		case 91: // '['
			throw new RuntimeException((new StringBuilder(
					"no arrayType excpected: ")).append(s).toString());

		case 84: // 'T'
			return true;
		}
		return false;
	}

	public static int getArrayDim(String type) {
		int dim;
		for (dim = 0; type.charAt(dim) == '['; dim++) {
		}
		return dim;
	}

	public void setEnvironment(IType type) {
		envClass = type;
	}

	public String resolveType(String string) throws JavaModelException {
		String resolvedType[][] = (String[][]) null;
		if (resolvedType == null) {
			return (new StringBuilder("fqn('")).append(string).append("')")
					.toString();
		} else {
			return (new StringBuilder("fqn('")).append(
					resolveClassNameFromSimpleName(string)).append("')")
					.toString();
		}
	}
}
